
type program =
  | Program of stmt list

and stmt = 
  | Break of string option
  | Continue of string option
  | Label of string * stmt
  | Return of expr
  | Var of expr
  | Expr of expr
  | Block of stmt list


and expr = 
  | PropSet of expr * string * expr
  | BinaryOp of string * expr * expr
  | UnaryOp of string * expr
  | Lambda of string list * stmt
  | Number of string
  | String of string
  | Regexp of string
  | Id of string
  | Call of expr * (expr list)
  | PropGet of expr * string
  | ObjectVal of (string * expr) list
  | ArrayVal of expr list
  | Null
  | Undefined
  | True
  | False
  | Select of (string * expr) list * (table_ref list) * expr
  | List of expr * expr

and table_ref = 
  | Table of string * string
  | LeftJoin of table_ref * table_ref * join_condition
  | RightJoin of table_ref * table_ref * join_condition
  | InnerJoin of table_ref * table_ref * join_condition
  | CrossJoin of table_ref * table_ref
  | NaturalLeftJoin of table_ref * table_ref
  | NaturalRightJoin of table_ref * table_ref

and join_condition = 
  | On of expr
  | Using of string list




let rec join delim f x = 
	match x with 
		[] -> "" 
	  | hd::[] -> (f hd)
	  | hd::tl -> (f hd) ^ delim ^ (join delim f tl)

let option_to_string = function
  | None -> ""
  | Some x -> x


let rec program_to_string = function
  | Program x -> join ";\n" stmt_to_string x

and stmt_to_string = function
  | Break None -> "break"
  | Break Some x -> "break " ^ x
  | Continue None -> "continue"
  | Continue Some x -> "continue " ^ x
  | Label (x, y) -> x ^ ":" ^ (stmt_to_string y)
  | Return x -> "return " ^ (expr_to_string x)
  | Var x -> "var " ^ (expr_to_string x)
  | Expr x -> expr_to_string x
  | Block x -> stmt_block_to_string x

and stmt_block_to_string = function x -> "{" ^ (join "; " stmt_to_string x) ^ "}"

and expr_to_string = function
  | List(x,y) -> "(" ^ (expr_to_string x) ^ "), (" ^ (expr_to_string y) ^ ")"
  | PropSet(x, y, z) -> (expr_to_string x) ^ "." ^ y ^ "=" ^ (expr_to_string z)
  | BinaryOp(op,x,y) -> (expr_to_string x) ^ " " ^ op ^ " (" ^ (expr_to_string y) ^ ")"
  | UnaryOp(x,y) -> "unary_" ^ x ^ "(" ^ (expr_to_string y) ^ ")"
  | Lambda(x, y) -> "function (" ^ (join ", " (function x->x) x) ^ ")" ^ (stmt_to_string y)
  | Number x -> x
  | String x -> x
  | Regexp x -> "/" ^ x ^ "/"
  | Id x -> x
  | Call(x,y) -> (expr_to_string x) ^ "(" ^ (join ", " expr_to_string y) ^ ")"
  | PropGet(x,y) -> (expr_to_string x) ^ "." ^ y
  | ObjectVal x -> join ", " (function (x,y) -> x ^ ":" ^ (expr_to_string y)) x
  | ArrayVal x -> join ", " expr_to_string x
  | Undefined -> "undefined"
  | Null -> "null"
  | True -> "true"
  | False -> "false"
  | Select (x,y,z) -> 
		"(" ^
			" SELECT " ^ (join ", " (function (x,y) -> x ^ (expr_to_string y)) x) ^ 
			" FROM " ^ (join " " table_ref_to_string y) ^ 
			" WHERE " ^ (expr_to_string z) ^
		" )"



and table_ref_to_string = function
  | Table (x,y) -> x ^ " AS " ^ y
  | LeftJoin (x,y,z) -> (table_ref_to_string x) ^ " LEFT JOIN " ^ (table_ref_to_string y) ^ (join_condition_to_string z)
  | RightJoin (x,y,z) -> (table_ref_to_string x) ^ " RIGHT JOIN " ^ (table_ref_to_string y) ^ (join_condition_to_string z)
  | InnerJoin (x,y,z) -> (table_ref_to_string x) ^ " INNER JOIN " ^ (table_ref_to_string y) ^ (join_condition_to_string z)
  | CrossJoin (x,y) -> (table_ref_to_string x) ^ " CROSS JOIN " ^ (table_ref_to_string y)
  | NaturalLeftJoin (x,y) -> (table_ref_to_string x) ^ " NATURAL LEFT JOIN " ^ (table_ref_to_string y)
  | NaturalRightJoin (x,y) -> (table_ref_to_string x) ^ " NATURAL RIGHT JOIN " ^ (table_ref_to_string y)

and join_condition_to_string = function
  | On x -> " ON " ^ (expr_to_string x)
  | Using x -> " USING (" ^ (join ", " (function x->x) x) ^ ")"






